#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Copyright 2014
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#codeby     道长且阻
#email      ydhcui@suliu.net/QQ664284092
#website    http://www.suliu.net

import os
import sys
import json
import random
import time
from hashlib import md5
import socket
import util
try:
  import urllib as parse
  import urllib2 as request
  import cookielib as cookiejar
  import Queue as queue
except ImportError as e:
  #print(e)
  from urllib import parse,request
  from http import cookiejar
  import queue

socket.setdefaulttimeout(120)


def inputbox(title=None,default=None):
    try:
      result = raw_input(title)
    except:
      result = input(title)
    return  result if result else default


class QQlogin(object):
    def _hexchar2bin(self, num):
        arry = bytearray()
        for i in range(0, len(num), 2):
            arry.append(int(num[i:i+2],16))
        return arry
    def _preprocess(self, password, verifycode):
        self.__hashpasswd = self._md5(password) #store hashed password
        I = self._hexchar2bin(self.__hashpasswd)
        H = self._md5(I + bytes(verifycode[2], encoding="ISO-8859-1"))
        G = self._md5(H + verifycode[1].upper())
        return G
    def _md5(self, s):
        try:
            s = s.encode('utf-8')
        finally:
            return md5(s).hexdigest().upper()

    def _preprocess2(self, password, verify):
        pwd = md5(md5(password).digest() + verify[2]).hexdigest().upper()
        pwd = md5(pwd + verify[1]).hexdigest().upper()
        return pwd

    def _request(self, url, data=None,cookie=1,timeout=0):
        if timeout:socket.setdefaulttimeout(timeout)  #设置超时
        if data:
            data = parse.urlencode(data).encode('utf-8')
            rr = request.Request(url, data, self._headers)
        else:
            rr = request.Request(url=url, headers=self._headers)
        try:
            fp=self.opener.open(rr)
        except Exception as e: #socket time out
            return e
        if fp:
            if fp.info().get('Content-Encoding') == 'gzip':
                '''
                py3k
                f = gzip.decompress(fp.read())
                res = f.decode('utf-8')
                -----------------------
                py2k
                try:
                  gf = GzipFile(fileobj=StringIO(fp.read()), mode="r")
                  f = gf.read()
                except:
                  f = gf.extrabuf
                res = f.decode('utf-8')
                '''
                pass
            else:
                res = fp.read()
                try:
                    res = res.decode('utf-8')
                except:
                    res = res
            ct = fp.info().get('Content-Type')
            if ct == 'text/plain; charset=utf-8' or ct == 'application/json;charset=utf-8':
                res = json.loads(res)
                if res['retcode'] == 0: #success
                    res = res['result']
                elif res['retcode'] == 103: #cookie timeout
                    self.login()
                elif res['retcode'] == 102: #ok
                    res = None
                elif res['retcode'] == 114: #send msg fail TODO:retry
                    res = None
                elif res['retcode'] == 116: #update ptwebqq value
                    self.logininfo.update({'ptwebqq':res['p']})
                    res = None
                elif res['retcode'] == 100002: #login with cookie
                    res = None
                else:
                    res = None#self.__del__()
            if cookie:
                self.cookieJar.save(self.COOKIE,ignore_discard=True, ignore_expires=True)
        return res

    def __init__(self, qq, pw):
        self.qq         = qq
        self._pw        = pw
        self.msgid      = random.randrange(1111111, 99999999)
        self.logininfo  = {}
        self.logininfo['appid']      = "1003903"
        self.logininfo['rand']       = random.Random().random()
        self.logininfo['clientid']   = random.randrange(11111111, 99999999)

        self.COOKIE = util.getpath("res/%swebqq.cookie"%self.qq)
        self.VERIFY = util.getpath("res/%swebqq.png"   %self.qq)

        self.cookieJar = cookiejar.MozillaCookieJar(self.COOKIE)
        self.opener = request.build_opener(request.HTTPCookieProcessor(self.cookieJar))
        self._headers = {
            "User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20111111 Firefox/14.0.1",
            "Accept-Language":"zh-cn,en;q=0.8,en-us;q=0.5,zh-hk;q=0.3",
            #"Accept-Encoding":"gzip;deflate",
            "Connection":"keep-alive",
            "Referer":"http://v.qq.com"
        }
        if os.path.isfile(self.COOKIE):
            os.remove(self.COOKIE)

    def _getverifycode(self):
        url = 'https://ssl.ptlogin2.qq.com/check?uin={0}&appid={1}&r={2}&&login_sig={3}'.format(self.qq, self.logininfo['appid'], self.logininfo['rand'],self.logininfo['loginsig'])\
        +'&js_ver=10052&js_type=0&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html'
        res = self._request(url = url)
        verify =  eval(res.split("(")[1].split(")")[0])
        verify = list(verify)
        if verify[0] == '1':
            img = "https://ssl.captcha.qq.com/getimage?aid=%s&r=%s&uin=%s"%(self.logininfo['appid'], self.logininfo['rand'], self.qq)
            open(self.VERIFY,"wb").write(self._request(img))
            print(u"请查看同文件夹下的%s.png文件。"%self.qq)
            verify[1] = inputbox("please input the verifycode: ").strip().upper()
        return verify

    def _getloginsig(self):
        import re
        url="https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&style=16&mibao_css=m_webqq&appid={0}".format(self.logininfo['appid']) \
           +"&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=2013102400"
        res=self._request(url)
        self.logininfo['loginsig'] = re.compile('g_login_sig=encodeURIComponent\("(.*?)"\);').findall(res)[0]

class QQapi(QQlogin):

    def __init__(self, qq, pw):
        super(QQapi, self).__init__(qq, pw)
        self.Queue = queue.Queue(0)
        self.sendtime = time.time()
        self.sendmsg = None
        self.friends = {}#好友列表
        self.groups  = {}#群组列表
        self.discus  = {}#讨论组列表

    def login(self):
        print('login...',self.qq)
        if os.path.isfile(self.COOKIE):
            self.cookieJar.load(self.COOKIE,ignore_discard=True, ignore_expires=True)
            print('login from cookies...',self.qq)
        else:
            self._getloginsig()
            self._verifycode = self._getverifycode()
            try:self.pswd = self._preprocess(self._pw, self._verifycode)#py3k
            except:self.pswd = self._preprocess2(self._pw, self._verifycode)#py2k
            self._headers.update({"Referer":"https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&style=5&mibao_css=m_webqq&appid={0}&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20130221001".format(self.logininfo['appid'])})
            url = "https://ssl.ptlogin2.qq.com/login?u={0}&p={1}&verifycode={2}&webqq_type=10&remember_uin=1&login2qq=1&aid={3}&login_sig={4}".format(self.qq, self.pswd, self._verifycode[1], self.logininfo['appid'],self.logininfo['loginsig']) \
                + "&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=27-64-95830&mibao_css=m_webqq&t=1&g=1&js_type=0&js_ver=10052"
            res = self._request(url=url)
            ptuiCB= eval(res.split("ptuiCB(")[1].split(");")[0])
            pb1,pb2,pburl,pb3,pbstatus,pbnick = ptuiCB
            if pbstatus.find("成功") != -1:
                self._request(pburl)
            elif pbstatus.find("验证码") != -1:
                print("验证码错误")
                self._getverifycode()
                self.login()
            else:
                print(ptuiCB)
        self.logininfo.update(dict([(x.name,x.value) for x in self.cookieJar]))
        self.get_login_info()
        self.poll()

    def get_login_info(self):
        try:
          url = "http://d.web2.qq.com/channel/login2"
          data = {'r':json.dumps({'status':'online','ptwebqq':self.logininfo['ptwebqq'],'clientid':self.logininfo['clientid']})}
          res = self._request(url, data)
          self.logininfo.update(res)
        except:
          os.remove(self.COOKIE)
          self.login()
          '''{"retcode":0,
              "result":{"uin":1149757929,
                        "cip":1851151858,
                        "index":1075,
                        "port":35925,
                        "status":"online",
                        "vfwebqq":"b9556a8b4f0cbdce9db2485a90a34c0c912b36d89badc6b224d16eacd25cd8a0c3857e3bcaefd2f",
                        "psessionid":"8368046764001d636f6e67365727665725f77656271714031302e3133392e372e31363000007f9100000841036e0400e9e987446d0000000a40543677333456357a676d00000028b9556a8b4f0cbdce9db2485a9c0a34c0c912b36d89badc6b224d16eacd25cd8a0c3857e3bcaefd2f",
                        "user_state":0,
                        "f":0}}
          '''
    def poll(self):
        """ 开始心跳"""
        while True:
            try:
                url = "http://d.web2.qq.com/channel/poll2"
                status = {'clientid':self.logininfo['clientid'],
                          'psessionid':self.logininfo['psessionid'],
                          'key':0,
                          'ids':[],
                          }
                data = {'r':json.dumps(status),
                        'clientid' : self.logininfo['clientid'],
                        'psessionid':self.logininfo['psessionid'],
                        }
                res = self._request(url,data)
                if res:
                    self.Queue.put(res)
                    print(time.time())
            except Exception as e:
                print(e)
                time.sleep(60)

    def makemsgid(self):
        self.msgid += 1
        return self.msgid
    def makefont(self,name='宋体',size='9',color='000000'):
      return ["font",{"name":name,"size":size,"style":[0,0,0],"color":color}]
    def makemsg(self,msg,face=None):
        #如果在短时间(3s)内发送的消息相同则剔除 不发送
        if time.time()-self.sendtime <= 3 and self.sendmsg==msg:
           return
        self.sendtime=time.time()
        self.sendmsg =str(msg)
        content=[]
        content.append(msg)
        if face:
          content.append(["face",face])
          content.append(" ")
        content.append(self.makefont())
        return json.dumps(content)

    def send_user_msg(self,uin,msg,face=None):
        """发送个人消息"""
        if not msg:return
        url = "http://d.web2.qq.com/channel/send_buddy_msg2"
        status = {'to':uin,
            'face':180,
            'content':self.makemsg(msg,face),
            'msg_id':self.makemsgid(),
            'clientid':self.logininfo['clientid'],
            "psessionid":self.logininfo['psessionid']
            }
        data = {'r':json.dumps(status),}
        res = self._request(url, data)
        return res == 'ok'

    def send_group_msg(self,uin,msg,face=None):
        """发送群消息"""
        if not msg:return
        url = "http://d.web2.qq.com/channel/send_qun_msg2"
        status = {"group_uin":uin,
                  "content":self.makemsg(msg,face),
                  "msg_id":self.makemsgid(),
                  "face":540,
                  "clientid":self.logininfo['clientid'],
                  "psessionid":self.logininfo['psessionid'],}
        data = {'r':json.dumps(status),}
        res = self._request(url, data)
        return res == 'ok'
    def send_discu_msg(self,uin,msg,face=None):
        """发送讨论组消息"""
        if not msg:return
        url = "http://d.web2.qq.com/channel/send_discu_msg2"
        status = {"did":uin,
            "content":self.makemsg(msg,face),
            "msg_id":self.makemsgid(),
            "clientid":self.logininfo['clientid'],
            "psessionid":self.logininfo['psessionid']
            }
        data = {'r':json.dumps(status)}
        res = self._request(url, data)
        return res == 'ok'

    def send_sess_msg(self,to, group_sig,msg,face=None):
        """发送临时消息"""
        if not msg:return
        url = "http://d.web2.qq.com/channel/send_sess_msg2"
        status={"to":to,
                "group_sig":"21d6a64c35598bfdcebe79104cd27703fd8d06c9e4f5ea6ed7fd7170b4d1e87ac943e9ab595644275a146ecc1064388d",
                "face":0,
                "content":self.makemsg(msg,face),
                "msg_id":self.makemsgid(),
                "service_type":0,
                "clientid":self.logininfo['clientid'],
                "psessionid":self.logininfo['psessionid'],
               }
        data = {'r':json.dumps(status),}
        res = self._request(url, data)
        return res=='ok'

    def get_uin2qq(self,uin):
        url="http://s.web2.qq.com/api/get_friend_uin2?tuin={0}&verifysession=&type=1&code=&vfwebqq={1}&t={2}".format(uin, self.logininfo['vfwebqq'], random.randrange(1345457600000,1345458000000))
        res = self._request(url)
        try:return res['account']
        except:pass

    def get_online(self):
        url = "http://d.web2.qq.com/channel/get_online_buddies2?clientid=%s&psessionid=%s"%(self.logininfo['clientid'],self.logininfo['psessionid'])
        res = self._request(url)
        return res

        """[{"uin":1998330330,"status":"online","client_type":1},...]
        """
    def get_friends(self):
        url = "http://s.web2.qq.com/api/get_user_friends2"
        data= {"r":json.dumps({"h":"hello","hash":self.hash0(self.qq,self.logininfo['ptwebqq']),"vfwebqq":self.logininfo['vfwebqq'],})}
        res = self._request(url,data)
        return res
        """{"friends":[{"flag":0,"uin":2228117076,"categories":0},],
            "marknames":[{"uin":2301788619,"markname":"罗家君","type":0},],
            "categories":[{"index":0,"sort":0,"name":"我的好友"},
                          {"index":1,"sort":1,"name":"未命名"}],
            "vipinfo":[{"vip_level":0,"u":2228117076,"is_vip":0},],
            "info":[{"face":147,"flag":8913410,"nick":"  oBenz","uin":2228117076},]
            }
        """

    def get_group_name_list(self):
        url  = "http://s.web2.qq.com/api/get_group_name_list_mask2"
        data = {'r':json.dumps({"vfwebqq":self.logininfo['vfwebqq']})}
        res  = self._request(url,data)
        return res

        """{"gmasklist":[],
            "gnamelist":[{"flag":1,"name":"溯流","gid":124222594,"code":2544571247},...],
            "gmarklist":[]
           }
        """

    def get_user_info(self,uin):
        url = "http://s.web2.qq.com/api/get_friend_info2?tuin=%s&verifysession=&code=&vfwebqq=%s&t=%s" \
              %(uin,self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
        res = self._request(url)
        return res
        """{"face":0,
            "birthday":{"month":8,"year":1998,"day":8},
            "occupation":"",
            "phone":"",
            "allow":0,
            "college":"",
            "uin":1985170374,
            "constel":7,
            "blood":0,
            "homepage":"",
            "stat":10,
            "vip_info":0,
            "country":"",
            "city":"",
            "personal":"输入‘帮助’查看更多功能。\r获取源码请联系 664284092。",
            "nick":" WallE",
            "shengxiao":3,
            "email":"",
            "client_type":41,
            "province":"",
            "gender":"female",
            "mobile":""
            }
        """
    def get_group_info(self,gcode):
        self._headers.update({"Referer":"http://s.web2.qq.com/proxy.html?v=20110412001&callback=1&id=3"})
        self._headers.update({"Origin": "http://s.web2.qq.com"})
        url="http://s.web2.qq.com/api/get_group_info_ext2?gcode=%s&vfwebqq=%s&cb=undefined&t=%s" \
            %(gcode,self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
        res= self._request(url)
        return res

        """{stats:[{client_type:1, uin:1945326622, stat:10}, {client_type:1, uin:2995370625, stat:10},…],…}
            cards: [{muin:698150405, card:流溯}, {muin:2164645040, card:兜兜}, {muin:1945326622, card:流溯},…]
            ginfo: {face:0, memo:欢迎各位到来，一起加入我们约会吧。愿你下一伴到来，祝你们幸福, class:10034, fingermemo:, code:1417043986,…}
            members: [{muin:2635661014, mflag:0}, {muin:1755940334, mflag:0}, {muin:2672949989, mflag:4},…]
            memo: "欢迎各位到来，一起加入我们约会吧。愿你下一伴到来，祝你们幸福"
            name: "我们约会吧"
            option: 2
            owner: 3180169428
            minfo: [{nick: vìГTЦ, province:, gender:unknown, uin:2635661014, country:, city:},…]
            stats: [{client_type:1, uin:1945326622, stat:10}, ]
            vipinfo: [{vip_level:0, u:2047269867, is_vip:0}, ]
        """


    def get_group_sig(self,gcode,to_uin):
        url="http://d.web2.qq.com/channel/get_c2cmsg_sig2?id={0}&to_uin={1}&service_type=0&clientid={2}&psessionid={3}&t={4}" \
            .format(gcode,to_uin,self.logininfo['clientid'],self.logininfo['psessionid'],random.randrange(1345457600000,1345458000000))
        res=self._request(url)
        return res
        """{"type":0,
            "value":"b5bb05faaef1e7106b8d17b4466322ab2ae69b25395e10910b4320ed331794e885dd69a4163180adbd85746151f9efaa",
            "flags":{"text":1,"pic":1,"file":1,"audio":1,"video":1}
            }
        """
    def allow_add_friend(self,uin):
        """加好友"""
        url="http://s.web2.qq.com/api/allow_added_request2"
        data={'r':json.dumps({"account":uin,"vfwebqq":self.logininfo['vfwebqq']})}
        res=self._request(url)
        return res
        """{result:0, client_type:41, account:1149757929, tuin:2428510851, stat:10}"""

    def shake(self,uin):
        """抖窗口"""
        url="http://d.web2.qq.com/channel/shake2?to_uin=%s&clientid=%s&psessionid=%s&t=%s"%(uin,self.logininfo['clientid'],self.logininfo['psessionid'],random.randrange(1345457600000,1345458000000))
        res=self._request(url)
        return res


    def get_group_pic(self,gid,uin,rip,port,fid,pic):
      url = "http://web2.qq.com/cgi-bin/get_group_pic?type=0&gid={0}&uin={1}&rip={2}&rport={3}&fid={4}&pic={5}&vfwebqq={6}&t={7}".format(gid,uin,rip,port,fid,pic,self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
    def get_offpic(self,filepath):
      url = "http://d.web2.qq.com/channel/get_offpic2?file_path=%2F{0}&f_uin={1}&clientid={2}&psessionid={3}"

    def get_single_long_nick(self,uin):
      url = "http://s.web2.qq.com/api/get_single_long_nick2?tuin=1381956409&vfwebqq=%s&t=%s"%(self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
      res = self._request(url)
      return json.loads(res)['lnick']
      '''{"retcode":0,"result":[{"uin":1381956409,"lnick":"   谁没受过伤？ 伤呀伤的就习惯了"}]}
      '''
    def get_qq_level(self,uin):
      url = "http://s.web2.qq.com/api/get_qq_level2?tuin=%s&vfwebqq=%s&t=%s"%(uin,self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
      res = self._request(url)
      return json.loads(res)
      '''{"retcode":0,"result":{"level":59,"days":3788,"hours":18668,"remainDays":52,"tuin":1381956409}}
      '''

if __name__=="__main__":
  import threading
  Q=QQapi(1149757929,'')
  def main():
    while True:
      print(str(Q.Queue.get()))
      inputbox('enter')
  threading.Thread(target=Q.login).start()
  threading.Thread(target=main).start()

